fix(openai): Guard against choices=None#6216
Merged
alexander-alderman-webb merged 13 commits intogetsentry:masterfrom May 8, 2026
Merged
fix(openai): Guard against choices=None#6216alexander-alderman-webb merged 13 commits intogetsentry:masterfrom
choices=None#6216alexander-alderman-webb merged 13 commits intogetsentry:masterfrom
Conversation
Some providers (e.g. OpenRouter) can return choices=None on upstream error responses. hasattr(response, 'choices') returns True even when the attribute is None, causing a TypeError when iterating. Add an explicit None check before iterating response.choices.
When a streaming chunk has choices=None, the existing hasattr check passes but the subsequent iteration raises TypeError. Inside capture_internal_exceptions() this is silently suppressed, causing the usage capture on the next line to be skipped — leaving token usage and response text missing from the Sentry span. Add explicit None checks in both the sync and async streaming iterators.
…getsentry#6202) The HTTP client span in the stdlib integration was finishing in `getresponse()`, which only waits for response headers, not the actual response. For chunked or large responses, the actual data transfer happens during `read()`, leaving that time uninstrumented. Defer span completion to `HTTPResponse.read()` for responses with a body (chunked or Content-Length > 0), with `HTTPResponse.close()` as a safety net for responses that are never read.⚠️ Note that this means we might report some requests to be longer than they actually were, since in some cases we only close them once the GC gets to them (and `close()` is called). In a sense we're essentially flipping the current situation (where we report requests to be much shorter than they are, since they don't include the response part) -- but the current situation was incorrect for all spans, while this `close()` fallback should hopefully only kick in for edge cases. Responses with no body (Content-Length: 0, HEAD, 204, 304) still finish the span immediately in `getresponse()`. * Closes getsentry#2277 * Closes https://linear.app/getsentry/issue/PY-159/sentry-does-not-fully-instrument-requests-library-requests --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Alex Alderman Webb <alexander.webb@sentry.io>
…ms` (getsentry#6233) We had multiple `envelopes_to_x` helpers around in tests for unwrapping metrics, logs, and spans. Replace them all with `capture_items`, which has the added benefit of being more true to the actual payload leaving the SDK, unlike `envelopes_to_x`, which used to modify some fields (setting them to `None` if they were not present, for instance). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Just moving one test case to another file.
### Description Reintroducing the argv integration port from getsentry#6147. Work on full support for array attributes in the product is still ongoing, but at least the trace will now load (the attribute won't be displayed), so we're not breaking anything with this change. #### Issues - Closes getsentry#5998 - Closes https://linear.app/getsentry/issue/PY-2300/migrate-argv-to-span-first #### Reminders - Please add tests to validate your changes, and lint your code using `tox -e linters`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
### Description See getsentry#6217 #### Issues * Resolves https://linear.app/getsentry/issue/PY-2407/force-a-new-segment-where-we-were-creating-transactions * Resolves getsentry#6217 #### Reminders - Please add tests to validate your changes, and lint your code using `tox -e linters`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
Contributor
|
Hi @cla7aye15I4nd, Thanks a lot for the PR! We seem to have pulled in a new mypy major so merging is blocked by our required checks (very unfortunate timing). |
### Description
Set
[`flag.evaluation.{key}`](https://getsentry.github.io/sentry-conventions/attributes/flag/#flag-evaluation-key)
as span attribute in span streaming mode.
Add a span streaming test variant to affected integrations:
- Unleash
- Statsig
- Launchdarkly
- OpenFeature
#### Issues
- Closes
https://linear.app/getsentry/issue/PY-2141/support-feature-flags-in-span-first
- Closes getsentry#5676
- Closes
https://linear.app/getsentry/issue/PY-2372/migrate-unleash-to-span-first
- Closes getsentry#6070
- Closes
https://linear.app/getsentry/issue/PY-2364/migrate-statsig-to-span-first
- Closes getsentry#6062
- Closes
https://linear.app/getsentry/issue/PY-2335/migrate-launchdarkly-to-span-first
- Closes getsentry#6033
- Closes
https://linear.app/getsentry/issue/PY-2344/migrate-openfeature-to-span-first
- Closes getsentry#6042
#### Reminders
- Please add tests to validate your changes, and lint your code using
`tox -e linters`.
- Add GH Issue ID _&_ Linear ID (if applicable)
- PR title should use [conventional
commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type)
style (`feat:`, `fix:`, `ref:`, `meta:`)
- For external contributors:
[CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md),
[Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord
community](https://discord.gg/Ww9hbqr)
…etsentry#6236) Changing `sentry_sdk.trace` to `sentry_sdk.traces.trace` introduced a whole avalanche of [typing issues](https://github.com/getsentry/seer/actions/runs/25495762136/job/74814929830?pr=6291). Adding the overloads we have for the original `trace`.
The field capturing the end timestamp of a span used to be called `timestamp` in legacy mode (on the `Span` class), with `start_timestamp` as its counterpart. In span streaming, we originally followed the same convention on the new `StreamedSpan` class, but there's actually no reason to and it'd be better to use `end_timestamp` instead: - the field on the serialized span v2 is actually called `end_timestamp` - `end_timestamp` is a clearer name in general, and complements `start_timestamp` nicely
1387d00 to
8a5b079
Compare
Contributor
Author
|
Thanks you ! I have fixed. |
alexander-alderman-webb
approved these changes
May 8, 2026
choices=None
640c48d
into
getsentry:master
150 of 151 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Some providers (e.g. OpenRouter) can return
choices=Noneon upstream error responses. The existinghasattr(response, "choices")check passes because the attribute exists — it just doesn't guard againstNone.This causes two distinct bugs:
1. Non-streaming: crash in caller
This propagates out of Sentry's patched
create()wrapper before the calling code ever receives the response, making it impossible to handle on the caller side without catching a bareTypeError.2. Streaming: silent data loss in span
Inside
_wrap_synchronous_completions_chunk_iteratorand_wrap_asynchronous_completions_chunk_iterator, the iteration is wrapped incapture_internal_exceptions(). Whenchoices=None, theTypeErroris silently suppressed — but this causes a premature exit from thewithblock, sostreaming_message_total_token_usage = x.usageis never reached. Token usage and response text are then missing from the Sentry span.Fix
Add an explicit
is not Noneguard alongside the existinghasattrcheck at all four affected locations (non-streaming, sync streaming, async streaming).Reproduction
Use the OpenAI SDK client pointed at OpenRouter (
base_url="https://openrouter.ai/api/v1"). When an upstream provider fails, OpenRouter returns a response withchoices=null, which triggers both bugs.